package com.dsz.usercenter.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dsz.usercenter.common.ErrorCode;
import com.dsz.usercenter.enums.TeamStatusEnum;
import com.dsz.usercenter.exception.BusinessException;
import com.dsz.usercenter.mapper.TeamMapper;
import com.dsz.usercenter.model.domain.Team;
import com.dsz.usercenter.model.domain.User;
import com.dsz.usercenter.model.domain.UserTeam;
import com.dsz.usercenter.model.request.*;
import com.dsz.usercenter.model.vo.TeamUserVO;
import com.dsz.usercenter.model.vo.UserVO;
import com.dsz.usercenter.service.TeamService;
import com.dsz.usercenter.service.UserService;
import com.dsz.usercenter.service.UserTeamService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author 23826
 * @description 针对表【team(队伍)】的数据库操作Service实现
 * @createDate 2024-02-24 22:49:14
 */
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team> implements TeamService {

    @Resource
    private UserService userService;
    @Resource
    private UserTeamService userTeamService;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private RedissonClient redissonClient;


    /**
     * 用户加入队伍
     * @param teamJoinRequest 队伍加入参数
     * @param currentUser 当前登录用户
     * @return 是否加入成功
     */
    @Override
    public boolean joinTeam(TeamJoinRequest teamJoinRequest, User currentUser) {
        //todo 将不涉及操作数据库的逻辑放到前面
        if (teamJoinRequest == null || currentUser == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        RLock lock = redissonClient.getLock("yupao:user:joinTeam:lock");
        try {
            if (lock.tryLock(Long.MAX_VALUE, -1, TimeUnit.SECONDS)) {
                //用户最多加入五个队伍
                Long userId = currentUser.getId();
                if (userId == null || userId <= 0) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR);
                }
                QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("userId", userId);
                long count = userTeamService.count(queryWrapper);
                if (count >= 5) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "加入队伍数已达上限");
                }
                //队伍必须存在
                Long teamId = teamJoinRequest.getTeamId();
                if (teamId == null || teamId <= 0) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR);
                }
                Team team = this.getById(teamId);
                if (team == null) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
                }
                //只能加入未满，未过期的队伍
                queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("teamId", teamId);
                long userCount = userTeamService.count(queryWrapper);
                if (userCount >= team.getMaxNum()) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍人数已满");
                }
                Date expireTime = team.getExpireTime();
                if (expireTime != null && new Date().after(expireTime)) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已过期");
                }
                //不能重复加入已加入的队伍
                queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("userId", userId).eq("teamId", teamId);
                long res = userTeamService.count(queryWrapper);
                if (res > 0) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "不能重复加入队伍");
                }
                //禁止加入私有的队伍
                TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(team.getStatus());
                if (TeamStatusEnum.PRIVATE.equals(teamStatusEnum)) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "无法加入私有队伍");
                }
                //如果加入的队伍是加密的，必须密码匹配才可以
                String password = teamJoinRequest.getPassword();
                if (TeamStatusEnum.SECRET.equals(teamStatusEnum)) {
                    if (StringUtils.isBlank(password) || !password.equals(team.getPassword())) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
                    }
                }
                UserTeam userTeam = new UserTeam();
                userTeam.setTeamId(teamId);
                userTeam.setUserId(userId);
                userTeam.setJoinTime(new Date());
                boolean result = userTeamService.save(userTeam);
                if (!result) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "加入队伍失败");
                }
                return true;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            //只能释放自己的锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return true;
    }

    /**
     * 创建队伍
     * @param teamAddRequest 队伍
     * @param request 请求
     * @return 队伍id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public long saveTeam(TeamAddRequest teamAddRequest, HttpServletRequest request) {
        //1. 请求参数是否为空？
        if (teamAddRequest == null || request == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //2. 是否登录，未登录不允许创建
        User currentUser = userService.getCurrentUser(request);
        final Long userId = currentUser.getId();
        Team team = new Team();
        team.setUserId(userId);
        BeanUtils.copyProperties(teamAddRequest, team);
        //3. 校验信息
        //  a. 队伍人数 > 1 且 <= 20
        int maxNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
        if (maxNum < 1 || maxNum > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍最大人数错误");
        }
        //  b. 队伍标题 <= 20
        String name = team.getName();
        if (StringUtils.isBlank(name) || name.length() > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍名错误");
        }
        //  c. 描述 <= 512
        String description = team.getDescription();
        if (StringUtils.isNotBlank(description) && description.length() > 512) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述错误");
        }
        //  d. status 是否公开（int）不传默认为 0（公开）
        Integer status = Optional.ofNullable(team.getStatus()).orElse(0);
        TeamStatusEnum enumByValue = TeamStatusEnum.getEnumByValue(status);
        if (enumByValue == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍状态错误");
        }
        //  e. 如果 status 是加密状态，一定要有密码，且密码 <= 32
        if (enumByValue.equals(TeamStatusEnum.SECRET)) {
            String password = team.getPassword();
            if (StringUtils.isBlank(password) || password.length() > 32) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍密码错误");
            }
        }
        //  f. 超时时间 > 当前时间
        Date expireTime = team.getExpireTime();
        if (new Date().after(expireTime)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "超时时间错误");
        }
        //  g. 校验用户最多创建 5 个队伍
        //todo 有bug，有可能同时创建很多队伍
        Long teamNum = this.countCreateTeamNumByUserId(userId);
        if (teamNum >= 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍数已达上限");
        }
        //4. 插入队伍信息到队伍表
        boolean res = this.save(team);
        if (!res) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "插入失败");
        }
        Long teamId = team.getId();
        //5. 插入用户 => 队伍关系到关系表
        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(userId);
        userTeam.setTeamId(teamId);
        userTeam.setJoinTime(new Date());
        res = userTeamService.save(userTeam);
        if (!res) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "插入失败");
        }
        //创建成功，返回队伍id
        return teamId;
    }

    /**
     * 删除队伍
     * @param id 队伍id
     * @return 是否删除成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeTeamById(Long id, User currentUser) {
        if (id == null || id <= 0 || currentUser == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //队伍是否存在
        Team team = this.getById(id);
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
        }
        //是否为队伍的队长
        if (!team.getUserId().equals(currentUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        //删除用户-队伍关系
        LambdaQueryWrapper<UserTeam> userTeamQueryWrapper = new LambdaQueryWrapper<>();
        userTeamQueryWrapper.eq(UserTeam::getTeamId, id);
        boolean remove = userTeamService.remove(userTeamQueryWrapper);
        if (!remove) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除失败");
        }
        //删除队伍
        boolean res = this.removeById(id);
        if (!res) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除失败");
        }
        return true;
    }

    /**
     * 更新队伍
     * @param teamUpdateRequest 队伍
     * @return 是否更新成功
     */
    @Override
    public boolean updateTeam(TeamUpdateRequest teamUpdateRequest, User currentUser) {
        if (teamUpdateRequest == null || currentUser == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //查询队伍是否存在
        Long teamId = teamUpdateRequest.getId();
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team team = this.getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "队伍不存在");
        }
        //只有管理员或者队伍的创建者可以修改
        if (!userService.isAdmin(currentUser) && !currentUser.getId().equals(team.getUserId())) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        //如果队伍状态改为加密，必须要有密码
        Integer status = teamUpdateRequest.getStatus();
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(status);
        if (TeamStatusEnum.SECRET.equals(teamStatusEnum)) {
            String password = teamUpdateRequest.getPassword();
            if (StringUtils.isBlank(password)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "缺少密码");
            }
        }
        BeanUtils.copyProperties(teamUpdateRequest, team);
        boolean res = this.updateById(team);
        if (!res) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新失败");
        }
        return true;
    }

    /**
     * 根据id查找队伍
     * @param id 队伍id
     * @return 队伍
     */
    @Override
    public Team getTeamById(Long id) {
        if (id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team team = this.getById(id);
        if (team == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        return team;
    }

    /**
     * 获取队伍列表
     * @param teamQueryRequest 队伍请求参数
     * @return 队伍列表
     */
    @Override
    public List<TeamUserVO> listTeam(TeamQueryRequest teamQueryRequest, boolean isAdmin) {
        if (teamQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //从请求对象中获取队伍名称等查询条件，如果存在则作为查询条件
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        //队伍id列表
        List idList = teamQueryRequest.getIdList();
        if (CollectionUtils.isNotEmpty(idList)) {
            queryWrapper.in("id", idList);
        }
        //描述
        String description = teamQueryRequest.getDescription();
        if (StringUtils.isNotBlank(description)) {
            queryWrapper.like("description", description);
        }
        //创建用户
        Long userId = teamQueryRequest.getUserId();
        if (userId != null && userId > 0) {
            queryWrapper.eq("userId", userId);
        }
        //状态
        Integer status = teamQueryRequest.getStatus();
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(status);
        if (teamStatusEnum == null) {
            teamStatusEnum = TeamStatusEnum.PUBLIC;
            if (isAdmin) {
                queryWrapper.in("status", Arrays.asList(TeamStatusEnum.PUBLIC.getValue(), TeamStatusEnum.SECRET.getValue(), TeamStatusEnum.PRIVATE.getValue()));
            } else {
                queryWrapper.in("status", Arrays.asList(TeamStatusEnum.PUBLIC.getValue(), TeamStatusEnum.SECRET.getValue()));
            }
        } else {
            queryWrapper.eq("status", teamStatusEnum.getValue());
        }
        //如果不是管理员并且查询的队伍不是公开的，报错
        if (!isAdmin && !teamStatusEnum.equals(TeamStatusEnum.PUBLIC)) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        //队伍名称
        String teamName = teamQueryRequest.getName();
        if (StringUtils.isNotBlank(teamName)) {
            queryWrapper.like("name", teamName);
        }
        //队伍最大人数
        Integer maxNum = teamQueryRequest.getMaxNum();
        if (maxNum != null && maxNum > 0) {
            queryWrapper.eq("maxNum", maxNum);
        }
        //根据description和name搜索
        String searchText = teamQueryRequest.getSearchText();
        if (StringUtils.isNotBlank(searchText)) {
            queryWrapper.and(q -> q.like("name", searchText).or().like("description", searchText));
        }
        //不展示已过期的队伍：要求过期时间大于当前时间或者没有设置过期时间
        queryWrapper.and(q -> q.gt("expireTime", new Date()).or().isNull("expireTime"));
        //查询
        List<Team> teamList = this.list(queryWrapper);
        //获取队伍的id列表
        List<Long> teamIdList = teamList.stream().map(Team::getId).collect(Collectors.toList());
        //根据队伍的id列表获取所有加入这些队伍的用户
        LambdaQueryWrapper<UserTeam> userTeamQueryWrapper = new LambdaQueryWrapper<>();
        userTeamQueryWrapper.in(UserTeam::getTeamId, teamIdList);
        List<UserTeam> userTeamList = userTeamService.list(userTeamQueryWrapper);
        //teamId --> 记录数
        Map<Long, List<UserTeam>> teamIdToUsers = userTeamList.stream().collect(Collectors.groupingBy(UserTeam::getTeamId));
        //将team转为teamuservo
        return teamList.stream().map((team) -> {
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team, teamUserVO);
            //添加创建用户信息
            Long id = team.getUserId();
            User user = userService.getById(id);
            if (user != null) {
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(user, userVO);
                teamUserVO.setUserVO(userVO);
            } else {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建用户不存在");
            }
            //添加已加入用户数
            teamUserVO.setHasJoinNum(teamIdToUsers.get(team.getId()).size());
            return teamUserVO;
        }).collect(Collectors.toList());
    }

    /**
     * 分页查询
     * @param teamQueryRequest 队伍请求参数
     * @return 队伍列表
     */
    @Override
    public List<Team> pageTeam(TeamQueryRequest teamQueryRequest) {
        if (teamQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team team = new Team();
        BeanUtils.copyProperties(teamQueryRequest, team);
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>(team);
        long pageNum = teamQueryRequest.getPageNum();
        long pageSize = teamQueryRequest.getPageSize();
        Page<Team> teamPage = this.page(new Page<>(pageNum, pageSize), queryWrapper);
        List<Team> teamList = teamPage.getRecords();
        if (teamList == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        return teamList;
    }

    /**
     * 用户退出队伍
     * @param teamQuitRequest 退出队伍请求类
     * @param currentUser 用户
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean quitTeam(TeamQuitRequest teamQuitRequest, User currentUser) {
        if (teamQuitRequest == null || currentUser == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //检查队伍是否存在
        Long teamId = teamQuitRequest.getTeamId();
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team team = this.getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
        }
        //校验当前用户是否加入队伍
        Long userId = currentUser.getId();
        if (userId == null || userId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        LambdaQueryWrapper<UserTeam> userTeamQueryWrapper = new LambdaQueryWrapper<>();
        userTeamQueryWrapper.eq(UserTeam::getUserId, userId).eq(UserTeam::getTeamId, teamId);
        UserTeam one = userTeamService.getOne(userTeamQueryWrapper);
        if (one == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户未加入队伍");
        }
        //如果队伍只有一人，直接解散
        userTeamQueryWrapper = new LambdaQueryWrapper<>();
        userTeamQueryWrapper.eq(UserTeam::getTeamId, teamId);
        long count = userTeamService.count(userTeamQueryWrapper);
        if (count == 1) {
            //删除用户-队伍关系数据
            boolean res = userTeamService.remove(userTeamQueryWrapper);
            //删除队伍
            this.removeById(teamId);
            return true;
        }
        //如果队伍还有其他人
        //a.如果是队长退出队伍，将队长给第二个加入的用户
        if (userId.equals(team.getUserId())) {
            userTeamQueryWrapper.last("order by id asc limit 2");
            List<UserTeam> list = userTeamService.list(userTeamQueryWrapper);
            //修改队长
            team.setUserId(list.get(1).getUserId());
            this.updateById(team);
        }
        //b.如果不是队长，直接退出
        //删除用户-队伍关系
        userTeamService.removeById(one);
        return true;
    }

    /**
     * 获取用户加入的队伍
     *
     * @param teamQueryRequest 队伍请求类
     * @param currentUser 用户
     * @return 队伍列表
     */
    @Override
    public List<TeamUserVO> listJoinTeam(TeamQueryRequest teamQueryRequest, User currentUser) {
        if (teamQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //获取所有的队伍id
        LambdaQueryWrapper<UserTeam> userTeamQueryWrapper = new LambdaQueryWrapper<>();
        userTeamQueryWrapper.eq(UserTeam::getUserId, currentUser.getId());
        List<UserTeam> list = userTeamService.list(userTeamQueryWrapper);
        List<Long> teamIdList = list.stream().map(UserTeam::getTeamId).collect(Collectors.toList());
        teamQueryRequest.setIdList(teamIdList);
        //获取结果
        return this.listTeam(teamQueryRequest, true);
    }

    /**
     * 统计用户创建的队伍数
     * @param id 用户id
     * @return 队伍数
     */
    public Long countCreateTeamNumByUserId(Long id) {
        if (id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", id);
        return this.count(queryWrapper);
    }
}




